package tech.cn.note.excel;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import cn.hutool.core.convert.NumberChineseFormatter;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import tech.cn.note.utils.BigDecimalUtil;
import tech.cn.note.utils.DateTimeUtils;

import static cn.hutool.core.text.CharSequenceUtil.NULL;
import static tech.cn.note.utils.BigDecimalUtil.isZero;

/***JXLS模板填充自定义函数*/
public class JXLSFunction {

    public static final String FUNCTION_NAME = "Function";

    private static final JXLSFunction jxlsFunction = new JXLSFunction();

    private JXLSFunction() {
    }

    public static JXLSFunction getInstance() {
        return jxlsFunction;
    }

    public String doNothing(Object value) {
        return value == null ? "" : String.valueOf(value);
    }

    public String dateFormat(Object value) {
        LocalDate resultDate = DateTimeUtils.toLocalDate(value);
        return resultDate == null ? "" : resultDate.toString();
    }

    public String dateFormat(Object value, String formatter) {
        DateTimeFormatter dateTimeFormatter = DatePattern.NORM_DATE_FORMATTER;
        if (StringUtils.isNotBlank(formatter)) {
            dateTimeFormatter = DateTimeFormatter.ofPattern(formatter);
        }
        LocalDate resultDate = DateTimeUtils.toLocalDate(value);
        return resultDate == null ? "" : resultDate.format(dateTimeFormatter);
    }

    public Integer daysBetween(Object value1, Object value2) {
        LocalDate startLocalDate = DateTimeUtils.toLocalDate(value1);
        LocalDate endLocalDate = DateTimeUtils.toLocalDate(value2);
        if (Objects.isNull(startLocalDate) || Objects.isNull(endLocalDate)) {
            return null;
        }
        return (int) (startLocalDate.toEpochDay() - endLocalDate.toEpochDay());
    }

    public static Object nvl(Object value1, Object value2) {
        if (Objects.isNull(value1) || StrUtil.isEmpty(value1.toString())) {
            return value2;
        } else {
            return value1;
        }
    }

    public static String divide(Object value1, Object value2) {
        return divide(value1, value2, 100);
    }

    public static String divide(Object value1, Object value2, Object value3) {
        BigDecimal dividend = BigDecimalUtil.parse(value1);
        BigDecimal divisor = BigDecimalUtil.parse(value2);
        if (Objects.isNull(dividend) || isZero(divisor)) {
            return "0";
        }

        int scale = 100;
        if (Objects.nonNull(value3)) {
            scale = (int) value3;
        }
        return dividend.divide(divisor, scale, RoundingMode.HALF_UP).toPlainString();
    }

    public String numNegate(Object value) {
        return BigDecimalUtil.parseOrZero(value).negate().toPlainString();
    }

    public String numFormat(Object value) {
        BigDecimal num = BigDecimalUtil.parseOrZero(value);
        return num.stripTrailingZeros().toPlainString();
    }

    public String numFormat(Object value, int pointNum) {
        BigDecimal num = BigDecimalUtil.parseOrZero(value);
        return num.setScale(pointNum, RoundingMode.HALF_UP).toPlainString();
    }

    public String numFormat(Object value, int pointNum, int tag) {
        BigDecimal num = BigDecimalUtil.parseOrZero(value);
        BigDecimal numTag = BigDecimal.valueOf(tag);
        return BigDecimalUtil.multiply(num, numTag).setScale(pointNum, RoundingMode.HALF_UP).toPlainString();
    }

    public String thdNum(Object value) {
        BigDecimal num = BigDecimalUtil.parseOrZero(value);
        return BigDecimalUtil.formatThdByPara(num);
    }

    public String thdNum(Object value, int pointNum) {
        BigDecimal num = BigDecimalUtil.parseOrZero(value);
        return BigDecimalUtil.formatThd(num, pointNum);
    }

    public String thdNum(Object value, int pointNum, int tag) {
        BigDecimal num = BigDecimalUtil.parseOrZero(value);
        BigDecimal numTag = BigDecimal.valueOf(tag);
        return BigDecimalUtil.formatThd(BigDecimalUtil.multiply(num, numTag), pointNum);
    }

    public String chineseMoney(Object value) {
        return value == null ? "" : NumberChineseFormatter.format(Double.parseDouble(value.toString()), true, true);
    }

    public String percent(Object value) {
        BigDecimal num = BigDecimalUtil.parse(value);
        return num == null ? "" : BigDecimalUtil.percentFormat(num, num.scale());
    }

    public String percent(Object value, int pointNum) {
        BigDecimal num = BigDecimalUtil.parse(value);
        return num == null ? "" : BigDecimalUtil.percentFormat(num, pointNum);
    }

    public String percentWithoutZero(Object value, int pointNum) {
        BigDecimal num = BigDecimalUtil.parse(value);
        return isZero(num) ? "" : BigDecimalUtil.percentFormat(num, pointNum);
    }

    public String thousandth(Object value) {
        BigDecimal num = BigDecimalUtil.parse(value);
        return num == null ? "" : BigDecimalUtil.thousandthFormat(num, num.scale());
    }

    public String thousandth(Object value, int pointNum) {
        BigDecimal num = BigDecimalUtil.parse(value);
        return num == null ? "" : BigDecimalUtil.thousandthFormat(num, pointNum);
    }

//    public String getEnumDesc(String enumPath, String name) throws ClassNotFoundException {
//        if (StrUtil.hasBlank(enumPath, name)) {
//            return null;
//        }
//        return EnumUtils.getDesc((Class<? extends Enum<?>>) Class.forName(enumPath), name);
//    }

    public String beanToList(Object data, String field, String delimiter) {
        List<String> list = obj2List(data, field);
        return String.join(StrUtil.blankToDefault(delimiter, ","), list);
    }

    public String beanToList(Object data, String field) {
        List<String> list = obj2List(data, field);
        return String.join(",", list);
    }

    private static List<String> obj2List(Object data, String field) {
        List<String> list = new ArrayList<>();
        // data为list
        if (data instanceof Collection) {
            list = ((List<?>) data).stream().map(v -> {
                try {
                    if (v instanceof Map) {
                        Map<String, Object> map = (Map<String, Object>) v;
                        Object mapField = map.get(field);
                        return mapField instanceof BigDecimal
                                ? ((BigDecimal) mapField).toPlainString()
                                : String.valueOf(mapField);
                    } else {
                        Field declaredField = v.getClass().getDeclaredField(field);
                        declaredField.setAccessible(true);
                        return declaredField.get(v).toString();
                    }
                } catch (NoSuchFieldException | IllegalAccessException ignored) {
                    // 不打日志了 避免刷屏
                    return null;
                }
            }).filter(v -> CharSequenceUtil.isNotBlank(v) && !NULL.equals(v)).collect(Collectors.toList());
        } else if (data instanceof String) {
            // data为字符串（由list转）
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                List<Map<String, Object>> mapList = objectMapper.readValue(data.toString(),
                        new TypeReference<List<Map<String, Object>>>() {
                        });
                list = mapList.stream()
                        .filter(map -> map.containsKey(field))
                        .map(map -> map.get(field) instanceof BigDecimal
                                ? ((BigDecimal) map.get(field)).toPlainString()
                                : String.valueOf(map.get(field))
                        )
                        .filter(v -> CharSequenceUtil.isNotBlank(v) && !NULL.equals(v))
                        .collect(Collectors.toList());
            } catch (JsonProcessingException e) {
                // 不打日志了 避免刷屏
            }
        }
        return list;
    }
}
